package org.remixlab.gnutopic.client;

import java.awt.Color;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;

import org.remixlab.gnutopic.pojo.ForumCategory;
import org.remixlab.gnutopic.pojo.ForumMessage;
import org.remixlab.gnutopic.pojo.ForumThread;

import codeanticode.glgraphics.GLModel;
import codeanticode.glgraphics.GLTexture;

import processing.core.PApplet;
import processing.core.PVector;
import remixlab.proscene.Scene;

/**
 * Clase encargada de pintar los mensajes y los hilos en la pantalla.
 * @author mauro
 *
 */
public class SpiralPainter{
	
	private SpiralVisualization papplet;
	
	private int width;
	private int height;
	
	private Date minThreadDate;
	private Date maxThreadDate;
	
	private float gradeConstant = 0.0004f;
	
	private double radiansByHour = 6.28;	//equivalente de radianes holas..
	
	//indica el número de puntos que forman la espiral
	private int spiralNumPoints = 0;
	
	private float h = 24f;	//variable usada para transformar de milli segundos a horas.
	
	private float velocity = 80f;
	
	private int visualizationNumber = 2;	//inidica la visualización que se esta mostrando.
	
	private Map<Float, Integer> threadsByPosMap;	//indica cuantos threads hay para esa misma fecha.
	
	//setters y getters
	public int getVisualizationNumber() {
		return visualizationNumber;
	}

	public void setVisualizationNumber(int visualizationNumber) {
		this.visualizationNumber = visualizationNumber;
	}

	public float getGradeConstant() {
		return gradeConstant;
	}

	public float getVelocity() {
		return velocity;
	}

	public void setVelocity(float velocity) {
		this.velocity = velocity;
	}

	public void setGradeConstant(float gradeConstant) {
		this.gradeConstant = gradeConstant;
	}

	public double getRadiansByHour() {
		return radiansByHour;
	}

	public void setRadiansByHour(double radiansByHour) {
		this.radiansByHour = radiansByHour;
	}

	public float getH() {
		return h;
	}

	public void setH(float h) {
		this.h = h;
	}

	public SpiralVisualization getPapplet() {
		return papplet;
	}

	public void setPapplet(SpiralVisualization papplet) {
		this.papplet = papplet;
	}

	public int getWidth() {
		return width;
	}

	public void setWidth(int width) {
		this.width = width;
	}

	public int getHeight() {
		return height;
	}

	public void setHeight(int height) {
		this.height = height;
	}
	
	public Date getMinThreadDate() {
		return minThreadDate;
	}

	public void setMinThreadDate(Date minThreadDate) {
		this.minThreadDate = minThreadDate;
	}

	public Date getMaxThreadDate() {
		return maxThreadDate;
	}

	public void setMaxThreadDate(Date maxThreadDate) {
		this.maxThreadDate = maxThreadDate;
	}
	//fin getters y setters
	
	/**
	 * función que calcula el radio para un angulo dado t.
	 * @param t el angulo para el que se calcula el radio
	 * @return el radio para el angulo dado.
	 */
	private float getSpiralRatius(float t){
		float result = 0;
		
		result = new Float(t*(getElipseRatius(t)));
		
		return result;
	}
	
	/**
	 * función que calcula el radio para un angulo dado t.
	 * @param t el angulo para el que se calcula el radio
	 * @return el radio para el angulo dado.
	 */
	private float getElipseRatius(float t){
		float result = 0;
		
		float a = 1f;
		float b = 0.8f;
		
		result = new Float((a*b)/Math.sqrt(Math.pow((b*Math.cos(t)),2)+Math.pow((a*Math.sin(t)),2)));
		
		return result;
	}
	

	/**
	 * Método generado para obtener la coordenada x
	 * @param t: el angulo 
	 * @param a: una constante.
	 * @return
	 */
	private float getX (float r, float t){
		float result = 0;
		
		result = new Float(r*Math.cos(t));
		
		return result;
	}
	
	/**
	 * @param r
	 * @param t
	 * @return
	 */
	private float getY (float r, float t){
		float result = 0;
		
		result = new Float(r*Math.sin(t));
		
		return result;
	}
	
	/**
	 * Retorna un ArrayList con los modelos para cada categoría.  
	 * @param categories
	 * @return
	 */
	public ArrayList<GLModel> initDrawCategories(ArrayList<ForumCategory> categories){
		
		ArrayList<GLModel> result = new ArrayList<GLModel>();
		
		//recorro las categorías....
		for(int i = 0; i<categories.size();i++){
			
			//obtengo la categoría...
			ForumCategory category = categories.get(i);
			
			System.out.println("i: "+i+"category: "+category.getName());
			
			TreeMap<Date, ForumThread> threads = category.getThreadsByDate();
			
			//obtengo las coordenadas para esta categoría
			float[] coords = getCoords(threads.values(),i+1,categories.size(), category.getMessagesNumber(), visualizationNumber);
			
			//asigno las coordenadas a la categoría
			category.setCoords(coords);
			
			int subcategoriesNumber = 0;
			
			if(category.getSubCategories()!=null && category.getSubCategories().size()>0){
				subcategoriesNumber = category.getSubCategories().size();
			}
			
			//obtengo los colores para esta categoría
			float[] colors = getColors(threads.values(),i+1,categories.size(), category.getMessagesNumber(), subcategoriesNumber);
			
			//asigno los colores a la categoría
			category.setColors(colors);
			
			//obtengo el modelo
			GLModel model = new GLModel(papplet, category.getMessagesNumber(), GLModel.POINT_SPRITES, GLModel.DYNAMIC);
			
			//creo la textura
			GLTexture tex = new GLTexture(papplet, "particle.png");
			
			//inicializo el modelo...
			model.initColors();
			
			model.updateVertices(coords);
			model.updateColors(colors);
			
			model.initTextures(1);
			model.setTexture(0, tex);
			
			float pmax = model.getMaxPointSize();
			
			model.setMaxSpriteSize(0.1f*pmax);
			
			//model.setSpriteSize(1);
			
			model.setBlendMode(PApplet.BLEND);
			
			//asigno el modelo a la categoría.
			category.setModel(model);
			
			result.add(model);
		}
		
		//aqui dibujo las lineas para cada hilo de la categoría
		for(int j = 0; j<categories.size();j++){
			//obtengo la categoría
			ForumCategory category = categories.get(j);
			
			System.out.println("j: "+j+"category: "+category.getName());

			TreeMap<Date, ForumThread> threads = category.getThreadsByDate();
			
			//creo el modelo
			GLModel lineModel = new GLModel(papplet,2*category.getMessagesNumber(),PApplet.LINE, GLModel.DYNAMIC);
			lineModel.initColors();
			
			//obtengo las coordenadas para esta categoría
			float[] lineCoords = getLinesCoords(threads.values(),j+1,categories.size(), category.getMessagesNumber(), visualizationNumber);
			
			category.setLineCoords(lineCoords);
			
			lineModel.updateVertices(lineCoords);
			
			int subcategoriesNumber = 0;
			
			if(category.getSubCategories()!=null && category.getSubCategories().size()>0){
				subcategoriesNumber = category.getSubCategories().size();
			}
			
			//obtengo el color para cada linea
			float[] lineColors = getLineColors(threads.values(),j+1,categories.size(), category.getMessagesNumber(), subcategoriesNumber);
			
			category.setLineColors(lineColors);
			
			//asigno el color al modelo de lineas
			lineModel.updateColors(lineColors);
			
			//asigno el modelo a la categoría
			category.setLineModel(lineModel);
			
			//agrego el modelo
			result.add(lineModel);
			
		}
		
		return result;
	}
	
	
	/**
	 * Método para actualizar la posición de las categorias dependiendo de
	 * los cambios en los parámetros.
	 * @param categories
	 */
	public boolean updateDots(ArrayList<ForumCategory> categories, boolean updateCoords, boolean updateColors){
		boolean result = false;
		
		float biggestMag = 0;
		
		//recorro las categorías....
		for(int i = 0; i<categories.size();i++){
			//obtengo la categoría...
			ForumCategory category = categories.get(i);
			
			//obtengo el modelo...
			GLModel model = category.getModel();
			
			TreeMap<Date, ForumThread> threads = category.getThreadsByDate();
			
			Collection<ForumThread> collectionThread = threads.values();
			
			if(updateCoords){
				
				//obtengo las coordenadas de la categoría...
				float[] currentCoords = category.getCoords();
				
				//aqui se almacenarán las futuras coordenadas...
				float[] futureCoords;
				
				if(category.isCalculateFutureCoords()){
					//ahora, obtengo las nuevas coordenadas....	
					futureCoords = getCoords(collectionThread,i+1,categories.size(), category.getMessagesNumber(), visualizationNumber);
					
					//ahora se la asigno a la categoria
					category.setFutureCoords(futureCoords);
					
					//indico que ya se calcularon las futuras coordenadas.
					category.setCalculateFutureCoords(false);
				}else{
					//obtengo las futuras categorías desde la categoría
					futureCoords = category.getFutureCoords();
				}
				
				Iterator<ForumThread> iterator = collectionThread.iterator();
				
				int j = 0;
				while(iterator.hasNext()){
					//obtengo el thread a dibujar..
					ForumThread thread = iterator.next();
					
					//obtengo los mensajes de este hilo...
					ArrayList<ForumMessage> messages = thread.getMessages();
					
					//recorro todos los mensajes del hilo
					for (ForumMessage message : messages){
						//asigno las coordenadas..
						int id = 0;
						
						if(message.getId()>=0){
							id = message.getId();
						}else{
							id = j;	
							
							//le asigno la posición i, como id del mensaje
							message.setId(id);
						}
						
						//obtengo las coordenadas x,y,z para el punto actual
						float xTemp = currentCoords[4*id+0];
						float yTemp = currentCoords[4*id+1];
						float zTemp = currentCoords[4*id+2];
						
						PVector currentLocation = new PVector(xTemp, yTemp, zTemp);
						
						if(currentLocation.mag()>biggestMag){
							biggestMag = currentLocation.mag();
						}
						
						xTemp = futureCoords[4*id+0];
						yTemp = futureCoords[4*id+1];
						zTemp = futureCoords[4*id+2];
						
						PVector futureLocation = new PVector(xTemp, yTemp, zTemp);
						
						//le resto este vector, para encontrar la distancia entre los dos
						futureLocation.sub(currentLocation);
						
						//ahora si la magnitud es mayor a 1 entonces...
						if(futureLocation.mag()>=velocity){
							//normalizo el vector...
							futureLocation.normalize();
							
							//multiplico el vector por una constante.. 
							futureLocation.mult(velocity);
							
							//sumo el vector a la posición actual.
							currentLocation.add(futureLocation);
							
							//actualizo el arrayList...
							
							currentCoords[4*id+0]=currentLocation.x;
							currentCoords[4*id+1]=currentLocation.y;
							currentCoords[4*id+2]=currentLocation.z;
							
							result = true;
							
						}else{
							if(futureLocation.mag()>0){
								
								currentLocation.add(futureLocation);
								
								currentCoords[4*id+0]=currentLocation.x;
								currentCoords[4*id+1]=currentLocation.y;
								currentCoords[4*id+2]=currentLocation.z;
								
							}
						}
						
						j++;
					}
				}	
				
				category.setCoords(currentCoords);
				
				model.updateVertices(currentCoords);
				
			}
			
			if(updateColors){
				float[] colors = getColors(collectionThread,i+1,categories.size(), category.getMessagesNumber(), visualizationNumber);
				
				//aqui actualizo el arreglo de la categoría.
				model.updateColors(colors);
				
				result = false;
			}
			
		}
		
		if(updateCoords){
			if(!result){
				for(int i = 0; i<categories.size();i++){
					//obtengo la categoría...
					ForumCategory category = categories.get(i);
					
					category.setCalculateFutureCoords(true);
				}
				
				//aqui modifico el radio del scene
				Scene scene = papplet.getScene();
				if(scene!=null && biggestMag>0){
					scene.setRadius(biggestMag);
				}
			}	
		}
		
		return result;
	}
	
	/**
	 * Método para actualizar la posición de las categorias dependiendo de
	 * los cambios en los parámetros.
	 * @param categories
	 */
	public boolean updateLines(ArrayList<ForumCategory> categories, boolean updateCoords, boolean updateColors){
		boolean result = false;
		
		float biggestMag = 0;
		
		//recorro las categorías....
		for(int i = 0; i<categories.size();i++){
			//obtengo la categoría...
			ForumCategory category = categories.get(i);
			
			//obtengo el modelo...
			GLModel model = category.getLineModel();
			
			TreeMap<Date, ForumThread> threads = category.getThreadsByDate();
			
			Collection<ForumThread> collectionThread = threads.values();
			
			//reviso si es necesario calcular las coordenadas o no...
			if(updateCoords){
				
				//obtengo las coordenadas de la categoría...
				float[] currentCoords = category.getLineCoords();
				
				//aqui se almacenarán las futuras coordenadas...
				float[] futureLineCoords;
				
				float[] futureCoords;
				
				if(category.isCalculateFutureCoords()){
					//ahora, obtengo las nuevas coordenadas....	
					futureLineCoords = getLinesCoords(collectionThread,i+1,categories.size(), category.getMessagesNumber(), visualizationNumber);
					
					//calculo las coordenadas de los puntos..
					futureCoords = getCoords(collectionThread,i+1,categories.size(), category.getMessagesNumber(), visualizationNumber);
					
					category.setFutureCoords(futureCoords);
					
					//ahora se la asigno a la categoria
					category.setFutureLineCoords(futureLineCoords);
					
					//indico que ya se calcularon las futuras coordenadas.
					category.setCalculateFutureCoords(false);
				}else{
					//obtengo las futuras categorías desde la categoría
					futureLineCoords = category.getFutureLineCoords();
				}
				
				Iterator<ForumThread> iterator = collectionThread.iterator();
				
				int j = 0;
				while(iterator.hasNext()){
					//obtengo el thread a dibujar..
					ForumThread thread = iterator.next();
					
					//obtengo los mensajes de este hilo...
					ArrayList<ForumMessage> messages = thread.getMessages();
					
					//recorro todos los mensajes del hilo
					for (ForumMessage message : messages){
						//asigno las coordenadas..
						
						//obtengo las coordenadas x,y,z para el punto 1
						float xTemp = currentCoords[4*j+0];
						float yTemp = currentCoords[4*j+1];
						float zTemp = currentCoords[4*j+2];
						
						PVector currentLocation = new PVector(xTemp, yTemp, zTemp);
						
						if(currentLocation.mag()>biggestMag){
							biggestMag = currentLocation.mag();
						}
						
						xTemp = futureLineCoords[4*j+0];
						yTemp = futureLineCoords[4*j+1];
						zTemp = futureLineCoords[4*j+2];
						
						PVector futureLocation = new PVector(xTemp, yTemp, zTemp);
						
						//le resto este vector, para encontrar la distancia entre los dos
						futureLocation.sub(currentLocation);
						
						//ahora si la magnitud es mayor a 1 entonces...
						if(futureLocation.mag()>=velocity){
							//normalizo el vector...
							futureLocation.normalize();
							
							//multiplico el vector por una constante.. 
							futureLocation.mult(velocity);
							
							//sumo el vector a la posición actual.
							currentLocation.add(futureLocation);
							
							//actualizo el arrayList...
							
							currentCoords[4*j+0]=currentLocation.x;
							currentCoords[4*j+1]=currentLocation.y;
							currentCoords[4*j+2]=currentLocation.z;
							
							result = true;
							
						}else{
							if(futureLocation.mag()>0){
								
								currentLocation.add(futureLocation);
								
								currentCoords[4*j+0]=currentLocation.x;
								currentCoords[4*j+1]=currentLocation.y;
								currentCoords[4*j+2]=currentLocation.z;
								
							}
						}
						
						//incremento el j una vez..
						j++;
						
						//obtengo las coordenadas x,y,z para el punto 2
						xTemp = currentCoords[4*j+0];
						yTemp = currentCoords[4*j+1];
						zTemp = currentCoords[4*j+2];
						
						currentLocation = new PVector(xTemp, yTemp, zTemp);
						
						if(currentLocation.mag()>biggestMag){
							biggestMag = currentLocation.mag();
						}
						
						xTemp = futureLineCoords[4*j+0];
						yTemp = futureLineCoords[4*j+1];
						zTemp = futureLineCoords[4*j+2];
						
						futureLocation = new PVector(xTemp, yTemp, zTemp);
						
						//le resto este vector, para encontrar la distancia entre los dos
						futureLocation.sub(currentLocation);
						
						//ahora si la magnitud es mayor a 1 entonces...
						if(futureLocation.mag()>=velocity){
							//normalizo el vector...
							futureLocation.normalize();
							
							//multiplico el vector por una constante.. 
							futureLocation.mult(velocity);
							
							//sumo el vector a la posición actual.
							currentLocation.add(futureLocation);
							
							//actualizo el arrayList...
							
							currentCoords[4*j+0]=currentLocation.x;
							currentCoords[4*j+1]=currentLocation.y;
							currentCoords[4*j+2]=currentLocation.z;
							
							result = true;
							
						}else{
							if(futureLocation.mag()>0){
								
								currentLocation.add(futureLocation);
								
								currentCoords[4*j+0]=currentLocation.x;
								currentCoords[4*j+1]=currentLocation.y;
								currentCoords[4*j+2]=currentLocation.z;
								
							}
						}
						
						j++;
					}
				}
				
				//aqui actualizo el arreglo de la categoría.
				category.setLineCoords(currentCoords);
				
				model.updateVertices(currentCoords);
			}
			
			//si debo actualizar los colores.. entonces...
			if(updateColors){
				//obtengo los colores...
				float[] colors = getLineColors(collectionThread,i+1,categories.size(), category.getMessagesNumber(), visualizationNumber);
				
				model.updateColors(colors);	
				
				result = false;
			}
		}
		
		return result;
	}	
	
	/**
	 * retorna un arreglo con las coordenadas de las lineas de guia para los mensajes.
	 * @param collectionThread
	 * @param categoryNumber
	 * @param categoriesNumber
	 * @param messagesNumber
	 * @param visualizationNumber
	 * @return
	 */
	private float[] getLinesCoords(Collection<ForumThread> collectionThread, int categoryNumber, int categoriesNumber, int messagesNumber, int visualizationNumber){
		
		//inicializo el hash map.. que revisa si hay varios hilos para el mismo tiempo..
		threadsByPosMap = new HashMap<Float, Integer>();
		
		//arreglo donde se almacenaran las coordenadas...
		float [] coords = new float[2*(4*messagesNumber)];
		
		Iterator<ForumThread> iterator = collectionThread.iterator();
		
		//variable que me indica en donde estoy en el arreglo de coordenadas.
		int k = 0;
		
		while(iterator.hasNext()){
			//obtengo el thread a dibujar..
			ForumThread thread = iterator.next();
			
			//dibujo el thread
			long timeThread = thread.getDate().getTime()-minThreadDate.getTime();
			
			//paso el tiempo de milisegundos a horas..
			long hours = millisToHours(timeThread);
			
			float t2 = 0;
			
			if(visualizationNumber==2 && categoriesNumber>0){
				//calculo el angulo dependiendo del número de categorías y del 
				//numero de la categoría.
				double d= (2*Math.PI)/categoriesNumber;
				
				float p = (float) d - (2*gradeConstant);
				//float p = (float) d;
				
				t2 = ((categoriesNumber*hours) + categoryNumber)*p;
				
			}else{
				//dibujo el hilo en el lugar que le corresponde
				double threadTime = hours*radiansByHour;
				
				//angulo en que se debe dibujar el thread
				t2 = new Double (threadTime).floatValue();
				
			}			
			
			float r= getSpiralRatius(t2);
			
			Integer b = 0;
			
			//guardo el valor con llave t2
			if(threadsByPosMap.get(t2)!=null){
				//obtengo el valor...
				b = threadsByPosMap.get(t2);
				
				//incremento el valor del hash Map
				b = b + 1;
				
				threadsByPosMap.put(t2, b);
				
			}else{
				//inicializo el valor en 1.
				threadsByPosMap.put(Float.valueOf(t2), Integer.valueOf(0));
			}
			
			//calculo el siguiente x, y
			float x = (0) + getX(r+(b*((1/h)*2000)), t2);
			float y = (0) + getY(r+(b*((1/h)*2000)), t2);
			
			//obtengo los mensajes de este hilo...
			ArrayList<ForumMessage> messages = thread.getMessages();
			long timeMessage = 0;
			
			float zOld = -1;
			
			int i = 0;
			//recorro todos los mensajes del hilo
			for (ForumMessage message : messages){
				
				//dibujo el thread
				timeMessage = message.getDate().getTime()-minThreadDate.getTime();
				
				//paso el tiempo de milisegundos a horas..
				hours = millisToHours(timeMessage);
				
				//calculo la altura del punto (la coordenada z
				if(visualizationNumber==2 && categoriesNumber>0){
					//calculo el angulo dependiendo del número de categorías y del 
					//numero de la categoría.
					double d= (2*Math.PI)/categoriesNumber;
					
					float p = (float) d - (2*gradeConstant);
					//float p = (float) d;
					
					t2 = ((categoriesNumber*hours) + categoryNumber)*p;
					
				}else{
					//dibujo el hilo en el lugar que le corresponde
					double threadTime = hours*radiansByHour;
					
					//angulo en que se debe dibujar el thread
					t2 = new Double (threadTime).floatValue();
					
				}
				
				//si estoy en la primera iteración... entonces..
				if(i<=0){
					zOld = t2;
				}else{
					//si es falso, entonces asigno las coordenadas..
					
					//agrego la coordenada anterior...
					coords[4*k+0]=x;
					coords[4*k+1]=y;
					coords[4*k+2]=zOld;
					
					coords[4*k+3]=1f;
					
					//incremento k una vez...
					k++;
					
					//ahora agrego la coordenada actual.
					coords[4*k+0]=x;
					coords[4*k+1]=y;
					coords[4*k+2]=t2;
					
					coords[4*k+3]=1f;
					
					//model.updateVertex(k, x, y, t2);
					
					//asigno a zOld el valor de t2
					zOld = t2;
					
					//incremento el valor de k
					k++;
				}
				
				//incremento el contador.
				i++;
			}
		}
		
		return coords;
		
	}
	
	private float[] getLineColors(Collection<ForumThread> collectionThread, int categoryNumber, int categoriesNumber, int messagesNumber, int subcategoriesNumber){
		//arreglo con los colores por mensaje
		float [] colors = new float[2*(4*messagesNumber)];
		
		Iterator<ForumThread> iterator = collectionThread.iterator();
		
		int k = 0;
		int j = 1;
		while(iterator.hasNext()){
			//obtengo el thread a dibujar..
			ForumThread thread = iterator.next();
			
			ForumCategory category = thread.getCategory();
			
			float red = 1;
			float blue = 1;
			float green = 1;
			
			float hue = 0;
			float sat = 0;
			
			if(category!=null){
				//aqui reviso si la categoría tiene los colores ya asignados o no..
				if(category.getHue()<=0f && 
					category.getSat()<=0f){
					//si los tres son menores o iguales a cero, significa que no se han llenado.
					
					//aqui asigno el color para la categoría...
					hue = ((float) categoryNumber)/ ((float) categoriesNumber);
					
					sat = 1;
					//aqui debo escoger la saturación del color.
					if(subcategoriesNumber>0){
						sat = ((float) j)/ ((float) subcategoriesNumber);
						j ++;
					}
					//modifico el color
					category.setHue(hue);
					
					//modifico la saturación
					category.setSat(sat);
					
				}else{
					hue = category.getHue();
					sat = category.getSat();
				}
			}else{
				System.out.println("categoría igual a nulo!!");
			}
			
			//aqui transformo de hsb a rgb...
			int rgb= Color.HSBtoRGB(hue, sat, 1f);
			
			//si no es visible, calculo el color nuevamente, pero con brillo igual a 0.1
			if(!thread.isVisibled()){
				rgb= Color.HSBtoRGB(hue, sat, 0.01f);
			}
			
			Color c = new Color(rgb);
			
			//obtengo los valores para los colores rojo, azul y verde.
			red = ((float) c.getRed())/ ((float) 255);
			
			blue = ((float) c.getBlue()) / ((float) 255);
			
			green = ((float) c.getGreen()) / ((float) 255);
			
			//obtengo los mensajes de este hilo...
			ArrayList<ForumMessage> messages = thread.getMessages();
			
			int i = 0;
			//recorro todos los mensajes del hilo
			for (ForumMessage message : messages){
				
				if(i>0){
					//agrego el color.
					colors[4 * k + 0] = red;
					colors[4 * k + 1] = green;
					colors[4 * k + 2] = blue;
					
					if(thread.isVisibled()){
						colors[4 * k + 3] = 0.1f;	
					}else{
						colors[4 * k + 3] = 0f;
					}
					
					k++;
					
					//incremento k en una unidad y agrego nuevamente el color
					colors[4 * k + 0] = red;
					colors[4 * k + 1] = green;
					colors[4 * k + 2] = blue;
					
					if(thread.isVisibled()){
						colors[4 * k + 3] = 0.1f;	
					}else{
						colors[4 * k + 3] = 0f;
					}
					
					k++;
				}
				
				//incremento el contador una vez...
				i++;
			}
		}
		
		return colors;
	}
	
	/**
	 * Retorna un arreglo de floats con las coordenadas de cada punto.
	 * @param collectionThread: los threads a dibujar
	 * @param categoryNumber: el numero de la categoría que se esta calculando 
	 * @param categoriesNumber: el numero total de categorías
	 * @param messagesNumber: el numero total de mensajes de toda la categoría.
	 * @return
	 */
	private float[] getCoords(Collection<ForumThread> collectionThread, int categoryNumber, int categoriesNumber, int messagesNumber, int visualizationNumber){
		
		//inicializo el hash map.. que revisa si hay varios hilos para el mismo tiempo..
		threadsByPosMap = new HashMap<Float, Integer>();
		
		//arreglo donde se almacenaran las coordenadas...
		float [] coords = new float[4*messagesNumber];
		
		Iterator<ForumThread> iterator = collectionThread.iterator();
		
		int i = 0;
		while(iterator.hasNext()){
			//obtengo el thread a dibujar..
			ForumThread thread = iterator.next();
			
			//dibujo el thread
			long timeThread = thread.getDate().getTime()-minThreadDate.getTime();
			
			//paso el tiempo de milisegundos a horas..
			long hours = millisToHours(timeThread);
			
			float t2 = 0;
			
			if(visualizationNumber==2 && categoriesNumber>0){
				//calculo el angulo dependiendo del número de categorías y del 
				//numero de la categoría.
				double d= (2*Math.PI)/categoriesNumber;
				
				float p = (float) d - (2*gradeConstant);
				//float p = (float) d;
				
				t2 = ((categoriesNumber*hours) + categoryNumber)*p;
				
			}else{
				//dibujo el hilo en el lugar que le corresponde
				double threadTime = hours*radiansByHour;
				
				//angulo en que se debe dibujar el thread
				t2 = new Double (threadTime).floatValue();
				
			}
			
			float maxZ = 0;
			float minZ = 0;
			
			float r= getSpiralRatius(t2);
			
			Integer b = 0;
			
			//guardo el valor con llave t2
			if(threadsByPosMap.get(t2)!=null){
				//obtengo el valor...
				b = threadsByPosMap.get(t2);
				
				//incremento el valor del hash Map
				b = b + 1;
				
				threadsByPosMap.put(t2, b);
				
			}else{
				//inicializo el valor en 1.
				threadsByPosMap.put(Float.valueOf(t2), Integer.valueOf(0));
			}
			
			//calculo el siguiente x, y
			//float x = (0) + getX(r, t2)+(int)(t2*Math.random()*0.1);
			//float y = (0) + getY(r, t2)+(int)(t2*Math.random()*0.1);
			
			//aqui incremento el angulo dependiendo si hay o no otro valor
			//float x = (0) + getX(r, (float)(t2+(b*0.1)));
			//float y = (0) + getY(r, (float)(t2+(b*0.1)));
			
			float x = (0) + getX(r+(b*((1/h)*2000)), t2);
			float y = (0) + getY(r+(b*((1/h)*2000)), t2);
			
			//float x = (0) + getX(r, t2);
			//float y = (0) + getY(r, t2);
			
			//obtengo los mensajes de este hilo...
			ArrayList<ForumMessage> messages = thread.getMessages();
			long timeMessage = 0;
			//recorro todos los mensajes del hilo
			for (ForumMessage message : messages){
				//asigno las coordenadas..
				int id = 0;
				
				if(message.getId()>=0){
					id = message.getId();
				}else{
					id = i;	
					
					//le asigno la posición i, como id del mensaje
					message.setId(id);
				}
				
				coords[4*id+0]=x;
				coords[4*id+1]=y;
				
				//dibujo el thread
				timeMessage = message.getDate().getTime()-minThreadDate.getTime();
				
				//paso el tiempo de milisegundos a horas..
				hours = millisToHours(timeMessage);
				
				//calculo la altura del punto (la coordenada z
				if(visualizationNumber==2 && categoriesNumber>0){
					//calculo el angulo dependiendo del número de categorías y del 
					//numero de la categoría.
					double d= (2*Math.PI)/categoriesNumber;
					
					float p = (float) d - (2*gradeConstant);
					//float p = (float) d;
					
					t2 = ((categoriesNumber*hours) + categoryNumber)*p;
					
				}else{
					//dibujo el hilo en el lugar que le corresponde
					double threadTime = hours*radiansByHour;
					
					//angulo en que se debe dibujar el thread
					t2 = new Double (threadTime).floatValue();
					
				}
				
				//agrego la coordenada z.
				coords[4*id+2]=t2;
				
				coords[4*id+3]=1f;
				
				//aqui reviso si es menor que minZ
				if(minZ >0){
					if(minZ>t2){
						minZ = t2;
					}
				}else{
					//si es falso, significa que no he inicializado minZ
					minZ = t2;
				}
				
				//hago lo mismo con el maxZ
				if(maxZ>0){
					if(maxZ<t2){
						maxZ=t2;
					}
				}else{
					maxZ = t2;
				}
				
				//incremento el contador.
				i++;
			}
			
			float h = maxZ - minZ; 
			
			//TODO: revisar el radio del punto... por ahora lo dejo en 1
			long w = 5;
			
			//si por alguna razón la altura del cilindro da 0 o menor, la dejo en 2
			if(h<=0){
				h = 5;
			}
			
			if(thread.getPapplet()==null){
				thread.setPapplet(papplet);
			}
			
			if(thread.getScene()==null){
				thread.setScene(papplet.getScene());
			}
			
			//cambio la posición del thread
			thread.init(x, y, minZ, w, h);
			
		}
		
		return coords;
		
	}
	
	/**
	 * Retorna un arreglo de floats con la info. del color para cada mensaje.
	 * @param collectionThread: los threads a dibujar
	 * @param categoryNumber: el numero de la categoría que se esta calculando 
	 * @param categoriesNumber: el numero total de categorías
	 * @param messagesNumber: el numero total de mensajes de toda la categoría.
	 * @return
	 */
	private float[] getColors(Collection<ForumThread> collectionThread, int categoryNumber, int categoriesNumber, int messagesNumber, int subcategoriesNumber){
		//arreglo con los colores por mensaje
		float [] colors = new float[4*messagesNumber];
		
		Iterator<ForumThread> iterator = collectionThread.iterator();
		
		int i = 0;
		int j = 1;
		while(iterator.hasNext()){
			//obtengo el thread a dibujar..
			ForumThread thread = iterator.next();
			
			ForumCategory category = thread.getCategory();
			
			float red = 1;
			float blue = 1;
			float green = 1;
			
			float hue = 0;
			float sat = 0;
			
			if(category!=null){
				//aqui reviso si la categoría tiene los colores ya asignados o no..
				if(category.getHue()<=0f && 
					category.getSat()<=0f){
					//si los tres son menores o iguales a cero, significa que no se han llenado.
					
					//aqui asigno el color para la categoría...
					hue = ((float) categoryNumber)/ ((float) categoriesNumber);
					
					sat = 1;
					//aqui debo escoger la saturación del color.
					if(subcategoriesNumber>0){
						sat = ((float) j)/ ((float) subcategoriesNumber);
						j ++;
					}
					//modifico el color
					category.setHue(hue);
					
					//modifico la saturación
					category.setSat(sat);
					
				}else{
					hue = category.getHue();
					sat = category.getSat();
				}
			}else{
				System.out.println("categoría igual a nulo!!");
			}
			
			//aqui transformo de hsb a rgb...
			int rgb= Color.HSBtoRGB(hue, sat, 1f);
			
			//si no es visible, calculo el color nuevamente, pero con brillo igual a 0.1
			/*if(!thread.isVisibled()){
				rgb= Color.HSBtoRGB(hue, sat, 0.01f);
			}*/
			
			Color c = new Color(rgb);
			
			//obtengo los valores para los colores rojo, azul y verde.
			red = ((float) c.getRed())/ ((float) 255);
			
			blue = ((float) c.getBlue()) / ((float) 255);
			
			green = ((float) c.getGreen()) / ((float) 255);
			
			//obtengo los mensajes de este hilo...
			ArrayList<ForumMessage> messages = thread.getMessages();
			
			//recorro todos los mensajes del hilo
			for (ForumMessage message : messages){
				
				int id = 0;
				
				if(message.getId()>=0){
					id = message.getId();
				}else{
					id = i;	
					
					//le asigno la posición i, como id del mensaje
					message.setId(id);
				}
				
				//agrego el color.
				colors[4 * id + 0] = red;
				colors[4 * id + 1] = green;
				colors[4 * id + 2] = blue;
				
				if(thread.isVisibled()){
					colors[4 * id + 3] = 1f;	
				}else{
					colors[4 * id + 3] = 0.009f;
				}
				
				
				//incremento el contador.
				i++;
			}
		}
		
		return colors;
	}
	
	/**
	 * Convierte de mili segundos a horas...
	 * @return
	 */
	private long millisToHours(long time){
		long result = 0;
		
		result = (long)(time/(1000*60*60*h));
		
		return result;
	}
	
	/**
	 * Dibuja una guia para conocer las fechas.
	 * @param n
	 */
	public void initGuide(int n){
		GLModel model;
		
		float p = 0.01f;
		
		//Creo un calendario con la fecha minima
		Calendar cal = Calendar.getInstance();
		cal.setTime(minThreadDate);
		
		java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd");
		
		//cambio el día a 1 e incremento el valor del mes en 1 unidad
		cal.set(Calendar.DATE, 1);
		cal.add(Calendar.MONTH, 1);
		
		System.out.println("Date is : " + sdf.format(cal.getTime()));
		
		
		spiralNumPoints = Math.round((float) (2*n*Math.PI)/p);
		
		int num = Math.round((float) (2*Math.PI)/p);
		
		model = new GLModel(papplet,2*spiralNumPoints,PApplet.LINE, GLModel.DYNAMIC);
		model.initColors();
		
		papplet.setSpiralModel(model);
		
		System.out.println("spiralNumPoints: "+spiralNumPoints);
		
		model.beginUpdateVertices();
		
		//inicializo las variables
		float x = 0;
		float y = 0;
		float z = 0;
		
		float r = 0;
		
		int c = 0;
		
		int a = 400;
		
		int j = 0;
		int i = 1;
		int k = 0;
		int t = 0;
		
		/*//calculo las variables con t igual a cero
		r = getElipseRatius(t*p);
		
		//calculo el siguiente x, y
		x = (0) + getX(i*a*r, t*p);
		y = (0) + getY(i*a*r, t*p);
		
		z = i*a;*/
		
		
		while(t<spiralNumPoints && i<=n){
			
			if(j<num){
			
				model.updateVertex(k, x, y, z);
				
				k++;
				
				//calculo el radio 
				r = getElipseRatius(t*p);
				
				//calculo el siguiente x, y
				x = (0) + getX(i*a*r, t*p);
				y = (0) + getY(i*a*r, t*p);
				
				z = i*a;
				
				model.updateVertex(k, x, y, z);
				
				k++;
				j++;
				
				t++;
				c++;
			}else{
				j = 0;
				i++;
				cal.add(Calendar.MONTH, 1);
				
			}
		}
		
		System.out.println("count despues del for: "+c);
		model.endUpdateVertices();
		
		//aqui actualizo el color
		model.beginUpdateColors();
		
		for (int q = 0; q<2*spiralNumPoints; q=q+1){
			model.updateColor(q, 255, 255, 255, 100);
		}
		
		model.endUpdateColors();
		
		
		
		
		
	/*	GLModel model;
		
		//float p = 0.1f; 	//indica la presición de la espiral (cada cuando se va a dibujar un punto)
		
		//calculo p dependiendo del número de brazos de la espiral
		//para ello divido 360 en el número de brazos.
		int b = 6;
		
		double d= (2*Math.PI)/b;
		
		float p = (float) d-(2*gradeConstant); 	//le quito un radian al resultado.
		
		spiralNumPoints = new Double ((2*n*Math.PI)/p).intValue();
		model = new GLModel(papplet,2*spiralNumPoints+1,PApplet.LINE, GLModel.DYNAMIC);
		model.initColors();
		
		papplet.setSpiralModel(model);
		
		System.out.println("spiralNumPoints: "+spiralNumPoints);
		
		model.beginUpdateVertices();
		
		//inicializo las variables
		float x = 0;
		float y = 0;
		float z = 0;
		
		float r = 0;
		
		int c = 0;
		
		for (int t = 0; t<2*spiralNumPoints; t=t+1){
			model.updateVertex(t, x, y, z);
			
			//calculo el radio 
			r = getRatius(t*p);
			
			//calculo el siguiente x, y
			x = (0) + getX(r, t*p);
			y = (0) + getY(r, t*p);
			
			z = z+ p;
			
			c ++;
		}
		System.out.println("count despues del for: "+c);
		model.endUpdateVertices();
		
		//aqui actualizo el color
		model.beginUpdateColors();
		
		c = 3;
		
		for (int t = 0; t<spiralNumPoints; t=t+1){
			model.updateColor(t, c*80, c*80, c*80, 80);
			if(c < b){
				c ++;
			}else{
				c = 1;
			}
		}
		
		model.endUpdateColors(); */
	}
}
